package muse.proxy;

/**
 * Activator.java
 * Thread che si occupa di istanziare e attivare un nuovo proxy
 * per ogni nuova richiesta ricevuta dal client
 * @author Ambra Montecchia
 * @version 1.2
 * */
import java.net.*;
import javax.media.Format;
import muse.proxy.connection.ConnectionManager;
import debug.DebugConsolle;
import parameters.Parameters;
import util.Logger;
import unibo.core.CircularBuffer;

// Marco: Activator sembra essere l'entità che sul relay istanzia e coordina i proxy
public class Activator extends Thread{
	
	//manager per la gestione della connessione UDP
	private ConnectionManager mng;
	//consolle di debug
	private DebugConsolle dc;
	
	/**
	 * Costruttore
	 * */
	public Activator(DebugConsolle cons){
		mng = new ConnectionManager();
		int res = 0;
		dc = cons;
		res = mng.openSocket(Parameters.PROXY_ACTIVATOR_PORT);
		while(res != 0){
			Logger.write("Tentativo di apertura socket activator fallito");
			Logger.write("Nuovo tentativo in corso");
			res = mng.openSocket(Parameters.PROXY_ACTIVATOR_PORT);
		}
		Logger.write("Socket activator aperta");
		dc.debugMessage("Activator: socket aperta sulla porta "+Parameters.PROXY_ACTIVATOR_PORT);
	}
	/**
	 * Metodo run
	 * */
	public void run(){
		int res = 0;
		//indice per la sequenza dei messaggi
		int indexClient = 0, indexReceived = 0, indexServer = 0;
		while(true){
			//ciclo di attesa di richieste di attivazione da parte dei client
			dc.debugMessage("Activator: in attesa di richieste da parte del client");
			res = mng.receivePacket();
			while(res != 0){
				res = mng.receivePacket();
			}
			//una volta ricevuto, il pacchetto viene letto
			DatagramPacket packet = mng.getPacket();
			try{
				MessageReader.readContent(packet);
			}
			catch(Exception ex){
				System.err.println("Impossibile leggere il contenuto della richiesta ricevuta dall'activator");
				ex.printStackTrace();
				continue;
			}
			int code = MessageReader.getCode();
			if(code == Parameters.ACTIVATE){
				//se il pacchetto contiene una richiesta di attivazione del proxy
				//Activator istanzia ProxyReceiver, ProxySender e ProxyController
				// Marco: sembra che sia il client con il messaggio Activate che specifica tutti i dati necessari per lo straming: Ip e porta del server
				// Ip del client, sue porte dell'Activator, di controllo e 	quella su cui fare lo streaming 
				// dimensione del buffer del client, nome della canzone desiderata
				// dopo aver recuperato tutti i dati trova delle porte libere per la ricezione e l'invio dello straming e per il controllo.
				dc.debugMessage("Activator: ricevuta richiesta attivazione");
				//primo parametro: IP del server
				String serverIP = MessageReader.getFirstParam();
				dc.debugMessage("Activator: IP server "+serverIP);
				//secondo parametro: porta del server
				String serverP = MessageReader.getSecondParam();
				int serverPort = Integer.parseInt(serverP);
				dc.debugMessage("Activator: porta server "+serverPort);				
				//invio messaggio di conferma attivazione del proxy |||| Marco: per me no: qui c'è il recupero dell'indirizzo e della porta
				// del client; nello specifico la porta è quella dell'activator del client
				InetAddress clientAddr = mng.getAddress();
				String clientIP = clientAddr.getHostAddress();
				dc.debugMessage("Activator: IP client "+clientIP);
				int clientActivatorPort = mng.getPort();
				dc.debugMessage("Activator: porta attivazione client "+clientActivatorPort);
				//terzo parametro: porta di controllo del client  Marco: la porta di controllo secondo me non è la porta di controllo rtp, ma la porta da cui si mandano i messaggi tipo "mandami altri frames"
				String cPort = MessageReader.getThirdParam();
				int clientControlPort = Integer.parseInt(cPort);
				dc.debugMessage("Activator: porta controllo client "+clientControlPort);
				//quarto parametro: porta di ricezione stream del client
				int clientRecStreamPort = Integer.parseInt(MessageReader.getFourthParam());
				dc.debugMessage("Activator: porta ricezione stream client "+clientRecStreamPort);
				//quinto parametro: dimensione del buffer
				int bufferSize = Integer.parseInt(MessageReader.getFifthParam());
				dc.debugMessage("Activator: dimensione client buffer "+bufferSize);
				//sesto parametro: nome del file
				String filename = MessageReader.getSixthParam();
				dc.debugMessage("Activator: file richiesto "+filename);
				//individuazione e occupazione porte libere
				int proxyRecStreamPort = mng.portScan();
				int proxySendStreamPort = mng.portScan();
				int proxyControlPort = mng.portScan();
				dc.debugMessage("Activator: recPort:"+proxyRecStreamPort+", sendPort:"+proxySendStreamPort+", ctrlPort:"+proxyControlPort);
				//incremento numero di sequenza dei messaggi ricevuti dal client
				indexReceived++;
				
				//messaggio di conferma al client attivazione, indica le porte usate dal proxy per lo streaming ed il controllo 
				try{
					
					DatagramPacket confirm = MessageFactory.buildConfirmActivation(indexClient,clientAddr, clientActivatorPort, proxySendStreamPort,proxyControlPort);
					indexClient++;
					mng.sendPacket(confirm);
					dc.debugMessage("Activator: inviata conferma attivazione al client");
				}
				catch(Exception ex){
					System.err.println("Impossibile inviare il messaggio di conferma attivazione");
				}
				
				//messaggio di richiesta file al server contiene le porte del proxy per la ricezione dello streaming ed il nome della canzone desiderata dal client
				try{
					InetAddress serverAdd = InetAddress.getByName(serverIP);
					DatagramPacket request = MessageFactory.buildFileRequest(indexServer, serverAdd, serverPort, proxyRecStreamPort, filename);
					indexServer++;
					mng.sendPacket(request);
					dc.debugMessage("Activator: inviata la richiesta al server");					
				}
				catch(Exception ex){
					dc.debugMessage("Impossibile inviare il messaggio di richiesta file");
					//TODO: stabilire cosa succede se non viene inviato il messaggio di richiesta al server
					//suggerimento: si invia una notifica di errore al client
					try{
						//messaggio di errata attivazione
						DatagramPacket confirm = MessageFactory.buildNoParamMessage(indexClient,clientAddr, clientActivatorPort,Parameters.ERROR);
						indexClient++;
						mng.sendPacket(confirm);
						//ritorna al ciclo principale di attesa delle richieste di attivazione
						continue;
					}
					catch(Exception innex){
						System.err.println("Impossibile inviare il messaggio di errore al client");
						continue;
					}
				}
				
				//attesa ricezione messaggio di conferma da parte del server
				int serverStreamP = 0;
				res = mng.receivePacket(); // la receive è bloccante
				//res = mng.receiveTimeoutPacket(Parameters.TIMEOUT);
				if(res == 0){
					//� stato ricevuto un messaggio nel timeout specificato
					DatagramPacket dp = mng.getPacket();
					try{
						
						MessageReader.readContent(dp);
						code = MessageReader.getCode();
						System.out.println("Codice messaggio: "+code);
						if(code == Parameters.CONFIRM_REQUEST){
							System.out.println("Ricevuta conferma da parte del proxy");
							//� stata ricevuta la conferma da parte del server contentete la porta dalla quale il server effettuerà lo streaming
							serverStreamP = Integer.parseInt(MessageReader.getFirstParam());
							System.out.println("Activator: ricevuta conferma da parte del server (Streaming Port:"+serverStreamP+")");
						}
					}
					catch(Exception ex){
						System.out.println(ex.getMessage());
						dc.debugMessage("Activator: problemi ricezione conferma da parte del server");
						//TODO: inviare notifica di errore al client
						try{
							//messaggio di errata attivazione
							DatagramPacket confirm = MessageFactory.buildNoParamMessage(indexClient,clientAddr, clientActivatorPort,Parameters.ERROR);
							indexClient++;
							mng.sendPacket(confirm);
							//ritorna al ciclo principale di attesa delle richieste di attivazione
							continue;
						}
						catch(Exception innex){
							System.err.println("Impossibile inviare il messaggio di errore al client");
						}
					}
				}
				else{ // qui  vuol dire che il valore di ritorno della receive è diverso da zero e che, quindi c'è stato un qualche errore.
					//invio messaggio di errore al client
					dc.debugMessage("Activator: timeout nella ricezione della conferma dal server");
					try{
						//messaggio di errore al client
						DatagramPacket confirm = MessageFactory.buildNoParamMessage(indexClient,clientAddr, clientActivatorPort,Parameters.ERROR);
						indexClient++;
						mng.sendPacket(confirm);
						//ritorna al ciclo principale di attesa delle richieste di attivazione
						continue;
					}
					catch(Exception ex){
						dc.debugMessage("Impossibile inviare il messaggio di errore al client");
						continue;
					}
					
				}
				
				//attivazione del thread per la ricezione
				System.out.println("Creazione proxy receiver");
				ProxyReceiver receiver = new ProxyReceiver(serverIP, serverStreamP, dc, proxyRecStreamPort, clientIP, clientRecStreamPort, proxySendStreamPort);
				//Marco: con questo metodo dovrei lanciare la ricezione del proxy receiver, almeno credo
				receiver.receive();
				CircularBuffer[] bufs = receiver.getBuffers();
				//dimensione del singolo frame
				int frameLen = (bufs[0].getFrame()).getLength();
				Format[] formats = receiver.getFormats();
				int bitRate = receiver.getBitRate();
				System.out.println("Bitrate: "+bitRate);
				try{
					//invio conferma al server
					InetAddress serverAdd = InetAddress.getByName(serverIP);
					
					DatagramPacket ack = MessageFactory.buildNoParamMessage(indexServer, serverAdd, serverPort,Parameters.ACK);
					indexServer++;
					mng.sendPacket(ack);
				}
				catch(Exception ex){
					System.err.println("Impossibile inviare il messaggio di conferma al server");
					//TODO: invio messaggio di errore al client
					try{
						//messaggio di errata attivazione
						DatagramPacket confirm = MessageFactory.buildNoParamMessage(indexClient,clientAddr, clientActivatorPort,Parameters.ERROR);
						indexClient++;
						mng.sendPacket(confirm);
						//ritorna al ciclo principale di attesa delle richieste di attivazione
						continue;
					}
					catch(Exception innex){
						System.err.println("Impossibile inviare il messaggio di conferma al server");
					}
				}
				//attivazione del thread per l'invio
				// TODO: capire perchè la creazione del thread che manda lo streaming al client è commentata
				//ProxySender sender = new ProxySender(clientIP, clientPort, bufs, formats, dc, sendingP);
				//inizializzazione e avvio del thread di controllo
				ProxyController ctrl = null;
				if(bitRate == -1){
					ctrl = new ProxyController(receiver, indexClient, indexReceived, proxyControlPort, clientIP, clientControlPort, receiver.getSender(), dc, bufferSize, frameLen, "");
				}
				else
					ctrl = new ProxyController(receiver, indexClient, indexReceived, proxyControlPort, clientIP, clientControlPort, receiver.getSender(), dc, bufferSize, bitRate, frameLen);
				int ret = 0;
				ret = ctrl.startControl();
				if(ret != 0){
					//l'attivazione del sender non � andata a buon fine
					//anche il receiver viene chiuso e le risorse vengono deallocate
					receiver.closeAll();
					//TODO: invio messaggio di errore al client e al server
					try{
						//messaggio di errata attivazione
						DatagramPacket confirm = MessageFactory.buildNoParamMessage(indexClient,clientAddr, clientActivatorPort,Parameters.ERROR);
						indexClient++;
						mng.sendPacket(confirm);
						//ritorna al ciclo principale di attesa delle richieste di attivazione
						continue;
					}
					catch(Exception innex){
						System.err.println("Impossibile inviare il messaggio di errore al client");
					}
				}
				System.out.println("Activator:ProxyController avviato");
				dc.debugMessage("Activator:ProxyController avviato");
			}
		}
	}

}
